package lsp.decoder;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import lsp.server.LspServer.LspConnection;

public class Scheduler {
	
	private Map<LspConnection, LspConnection> workersTable = new HashMap<>();

	public void addWorker(LspConnection worker) {
		System.out.println("Add worker: " + worker.getConnectionId());
		workersTable.put(worker, null);
	}

	public void crack(LspConnection requester, String hash, int lower, int upper) {
		LoadBalancer balancer = new LoadBalancer(requester, hash, lower, upper);
		balancer.execute();
	}

	private Set<Map.Entry<LspConnection, LspConnection>> getIdleWorkers() {
		Set<Map.Entry<LspConnection, LspConnection>> idleWorkers = new HashSet<>();
		for (Map.Entry<LspConnection, LspConnection> workerEntry: workersTable.entrySet()) {
			if (workerEntry.getValue() == null) {
				idleWorkers.add(workerEntry);
			}
		}
		return idleWorkers;
	}

	public LspConnection getClientByWorker(LspConnection worker) {
		return workersTable.get(worker);
	}

	public void releaseWorker(LspConnection worker) {
		workersTable.put(worker, null);
	}

	public List<LspConnection> getWorkersFromRequester(LspConnection requester) {
		List<LspConnection> workers = new ArrayList<>();
		for (Map.Entry<LspConnection, LspConnection> workerEntry: workersTable.entrySet()) {
			if (requester.equals(workerEntry.getValue())) {
				workers.add(workerEntry.getKey());
			}
		}
		return workers;
	}
	
	class LoadBalancer {
		private String hash;
		private int from;
		private int to;
		private Set<Map.Entry<LspConnection, LspConnection>> idleWorkers;
		private LspConnection requester;

		public LoadBalancer(LspConnection requester, String hash, int from, int to) {
			this.requester = requester;
			this.hash = hash;
			this.from = from;
			this.to = to;
			this.idleWorkers = getIdleWorkers();
		}
		
		public void execute() {
			int i = 0;
			for (Map.Entry<LspConnection, LspConnection> workerEntry: idleWorkers) {
				int nextI = Math.min(i + step(), to);

				workerEntry.setValue(requester);
				LspConnection worker = workerEntry.getKey();
				String message = String.format("C %s %d %d", hash, i, nextI);
				System.out.println("Repassing To worker " + worker.getConnectionId() + ": " + message);
				worker.sendData(message.getBytes());
				
				i = nextI;
			}
		}

		private int step() {
			return ((to - from) / idleWorkers.size())+1;
		}
	}

}
